(function() {
	var oncontextmenu = document.oncontextmenu;
	var LBUTTON = 0, MBUTTON = 1, RBUTTON = 2;
	var LEFT = 'L', RIGHT = 'R', UP = 'U', DOWN = 'D';
	var path, endtm, lastX, lastY, accuX, accuY;
	var lean = 1 / Math.tan(30 * Math.PI / 180);	// lean tolerance is 60 degrees
	var in_config = (chrome.extension.getURL('options.html') === location.href);
	var port = chrome.extension.connect({name: 'stroke.html-stroke'});

	var TRIGGER = RBUTTON, TIMEOUT = 200, MINSTEP = 20;

	/* calculate weight by accumulated distances moved in both X and Y */
	var weigh = function(x, y) {
		return Math.round(Math.sqrt(x * x + y * y));
	}

	document.addEventListener('mousedown', function(ev) {
		if (ev.button != TRIGGER) return;

		/* restore right click handler */
		if (TRIGGER == RBUTTON) {
			document.oncontextmenu = oncontextmenu;
		}

		/* start recording, reset everything */
		path = {count: 0, strokes: [], weights: []};
		lastX = ev.screenX, lastY = ev.screenY;
		accuX = accuY = 0;
	}, false);

	document.addEventListener('mousemove', function(ev) {
		if (ev.button != TRIGGER || !path) return;

		var stroke, x = ev.screenX, y = ev.screenY,
			offsetX = x - lastX, offsetY = y - lastY,
			absX = Math.abs(offsetX), absY = Math.abs(offsetY);

		if (absX < MINSTEP && absY < MINSTEP) return;
		lastX = x, lastY = y;
		accuX += absX, accuY += absY;

		/* ignore leaning, blurrer strokes are more recognizable */
//		if (0 == absY || (absX / absY > lean)) {
		if (absX > absY) {
			stroke = offsetX > 0 ? RIGHT : LEFT;
//		} else if (0 == absX || (absY / absX > lean)) {
		} else {
			stroke = offsetY > 0 ? DOWN : UP;
//		} else {
//			/* leaned strokes are always start with horizontal direction */
//			stroke = (offsetX > 0 ? RIGHT : LEFT) + (offsetY > 0 ? DOWN : UP);
		}
		if (0 == path.count || path.strokes[path.count - 1] != stroke) {
			accuX = absX, accuY = absY;
			++path.count;
			path.strokes.push(stroke);
			path.weights.push(weigh(accuX, accuY));
		} else {
			/* update weight */
			path.weights[path.count - 1] = weigh(accuX, accuY);
		}
		endtm = new Date().getTime();	// when mouse stops
	}, false);

	document.addEventListener('mouseup', function(ev) {
		if (ev.button != TRIGGER || !path || !path.count || (new Date().getTime() - endtm) > TIMEOUT) return;

		/* disable right click if the trigger button is right button */
		if (TRIGGER == RBUTTON) {
			document.oncontextmenu = function() { return false };
		}

		if (in_config) {
			/* post event for options.html */
			var e = document.createEvent("Event");
			e.initEvent("set-stroke-event", false, false);
			e.strokes = path.strokes;
			document.dispatchEvent(e);
		} else {
			/* post regular strokes */
			port.postMessage(path.strokes);
		}
	}, true);

	/* wait for the background page to push configurations */
	chrome.extension.onConnect.addListener(function(port) {
		if ('stroke.js-config' != port.name) return;

		port.onMessage.addListener(function(config) {
			TRIGGER = config.trigger;
			TIMEOUT = config.timeout;
			MINSTEP = config.minstep;
		});
	});

	/* execute code locally */
	chrome.extension.onConnect.addListener(function(port) {
		if ('stroke.js-script' != port.name) return;

		port.onMessage.addListener(function(script) {
			try {
				eval(script);
			} catch (ex) {
				console.error(ex);
			}
		});
	});
})();
